﻿using System;
using System.Collections;
using System.Collections.Generic;

namespace Pfz.Threading.Cooperative
{
	/// <summary>
	/// Class that calls another method allowing it to "yield return" at any moment
	/// then continue later.
	/// The actual implementation uses another thread, but if this resource is added
	/// in the .Net in the future, it will use the correct one.
	/// </summary>
	public sealed class StackSaver:
		DisposeAssurerBase<UnsafeStackSaver>,
		IEnumerable<bool>,
		IEnumerator<bool>
	{
		/// <summary>
		/// Gets the StackSaver that manages the actual method.
		/// May return null if this method is not managed by any stack-saver.
		/// </summary>
		public static UnsafeStackSaver Current
		{
			get
			{
				return UnsafeStackSaver.Current;
			}
		}

		/// <summary>
		/// Gets the value producer StackSaver that manages the actual method.
		/// </summary>
		public static IValueProducerStackSaver CurrentValueProducer
		{
			get
			{
				return UnsafeStackSaver.CurrentValueProducer;
			}
		}

		/// <summary>
		/// Yield returns to the method that called the current stack-saver, considering
		/// it does not expect any values.
		/// </summary>
		public static void StaticYieldReturn()
		{
			UnsafeStackSaver.StaticYieldReturn();
		}

		/// <summary>
		/// Yield returns to the method that called the current stack-saver, 
		/// casting the value if needed.
		/// </summary>
		/// <typeparam name="T"></typeparam>
		/// <param name="value"></param>
		public static void StaticYieldReturn<T>(T value)
		{
			UnsafeStackSaver.StaticYieldReturn(value);
		}

		private StackSaver(UnsafeStackSaver unsafeStackSaver):
			base(unsafeStackSaver)
		{
		}

		/// <summary>
		/// Creates a stack-saver for the given action.
		/// </summary>
		public StackSaver(Action action):
			base(new UnsafeStackSaver(action))
		{
		}

		/// <summary>
		/// Gets the exception that finished this stack-saver.
		/// If it finished normally or is still running, this will return null.
		/// </summary>
		public Exception Exception
		{
			get
			{
				return Value.Exception;
			}
		}

		/// <summary>
		/// Begins executing the action of the stack saver while still allowing the actul
		/// method to run.
		/// </summary>
		public void BeginExecute()
		{
			Value.BeginExecute();
		}

		/// <summary>
		/// Waits until the action executed by a BeginExecute is yielded or finishes.
		/// Returns true if the action was yielded (so it could return), false otherwise.
		/// </summary>
		public bool EndExecute()
		{
			return Value.EndExecute();
		}

		/// <summary>
		/// Waits until the action executed by a BeginExecute is yielded or finishes.
		/// If the action finishes by an exception, the exception is stored in the
		/// Exception property but is not rethrown.
		/// Returns true if the action was yielded (so it could return), false otherwise.
		/// </summary>
		public bool EndExecuteNoException()
		{
			return Value.EndExecuteNoException();
		}

		/// <summary>
		/// Executes the action until it finishes or yields.
		/// Returns true if it was yielded and can be continued, false otherwise.
		/// </summary>
		public bool Execute()
		{
			return Value.Execute();
		}

		/// <summary>
		/// Executes the action until it finishes or yields.
		/// Returns true if it was yielded and can be continued, false otherwise.
		/// If the action finishes by an exception, it is stored in the Exception property
		/// but it is not rethrown.
		/// </summary>
		public bool ExecuteNoException()
		{
			return Value.ExecuteNoException();
		}

		/// <summary>
		/// This method stops the execution of the actual action and returns to the caller,
		/// but allows the method to be continued later by a call to Execute.
		/// </summary>
		public void YieldReturn()
		{
			Value.YieldReturn();
		}

		IEnumerator<bool> IEnumerable<bool>.GetEnumerator()
		{
			return this;
		}

		IEnumerator IEnumerable.GetEnumerator()
		{
			return this;
		}

		bool IEnumerator<bool>.Current
		{
			get
			{
				return true;
			}
		}

		object IEnumerator.Current
		{
			get
			{
				return UnsafeStackSaver.BoxedTrue;
			}
		}

		bool IEnumerator.MoveNext()
		{
			return Value.Execute();
		}

		void IEnumerator.Reset()
		{
			throw new NotSupportedException();
		}
	}

	/// <summary>
	/// Class that calls another method allowing it to "yield return" values at any 
	/// moment then continue later.
	/// The actual implementation uses another thread, but if this resource is added
	/// in the .Net in the future, it will use the correct one.
	/// </summary>
	public sealed class StackSaver<T> :
		DisposeAssurerBase<UnsafeStackSaver<T>>,
		IEnumerator<T>,
		IEnumerable<T>,
		IValueProducerStackSaver
	{
		/// <summary>
		/// Gets the StackSaver that manages the actual method.
		/// May return null if this method is not managed by any stack-saver.
		/// </summary>
		public static UnsafeStackSaver<T> Current
		{
			get
			{
				return UnsafeStackSaver<T>.Current;
			}
		}

		private StackSaver(UnsafeStackSaver<T> unsafeStackSaver) :
			base(unsafeStackSaver)
		{
		}

		/// <summary>
		/// Creates a stack-saver for the given action.
		/// </summary>
		public StackSaver(Action action) :
			base(new UnsafeStackSaver<T>(action))
		{
		}

		/// <summary>
		/// Gets the exception that finished this stack-saver.
		/// If it finished normally or is still running, this will return null.
		/// </summary>
		public Exception Exception
		{
			get
			{
				return Value.Exception;
			}
		}

		/// <summary>
		/// Begins executing the action of the stack saver while still allowing the actul
		/// method to run.
		/// </summary>
		public void BeginExecute()
		{
			Value.BeginExecute();
		}

		/// <summary>
		/// Begins executing the action of the stack saver while still allowing the actul
		/// method to run.
		/// </summary>
		public bool EndExecute()
		{
			return Value.EndExecute();
		}

		/// <summary>
		/// Waits until the action executed by a BeginExecute is yielded or finishes.
		/// If the action finishes by an exception, the exception is stored in the
		/// Exception property but is not rethrown.
		/// Returns true if the action was yielded (so it could return), false otherwise.
		/// </summary>
		public bool EndExecuteNoException()
		{
			return Value.EndExecuteNoException();
		}

		/// <summary>
		/// Executes the action until it finishes or yields.
		/// Returns true if it was yielded and can be continued, false otherwise.
		/// </summary>
		public bool Execute()
		{
			return Value.Execute();
		}

		/// <summary>
		/// Executes the action until it finishes or yields.
		/// Returns true if it was yielded and can be continued, false otherwise.
		/// If the action finishes by an exception, it is stored in the Exception property
		/// but it is not rethrown.
		/// </summary>
		public bool ExecuteNoException()
		{
			return Value.ExecuteNoException();
		}

		/// <summary>
		/// This method stops the execution of the actual action and returns a value to the caller,
		/// but allows the method to be continued later by a call to Execute.
		/// </summary>
		public void YieldReturn(T value)
		{
			Value.YieldReturn(value);
		}

		void IValueProducerStackSaver.YieldReturn(object value)
		{
			Value.YieldReturn((T)value);
		}

		IEnumerator IEnumerable.GetEnumerator()
		{
			return this;
		}

		object IEnumerator.Current
		{
			get
			{
				return Value.CurrentValue;
			}
		}

		bool IEnumerator.MoveNext()
		{
			return Value.Execute();
		}

		void IEnumerator.Reset()
		{
			throw new NotSupportedException();
		}

		IEnumerator<T> IEnumerable<T>.GetEnumerator()
		{
			return this;
		}

		T IEnumerator<T>.Current
		{
			get
			{
				return Value.CurrentValue;
			}
		}
	}
}
